In C#, there is a default ColorMap class, defined in the System.Drawing.Imaging namespace. This class defines a mapping between existing colors and the new colors to which they are to be converted. When the map is applied, any pixel of the old color is converted to the new color. This class is useful for image processing applications.
However, in some graphics and chart applications, you may need the custom color maps to achieve specific visual effects. These color maps are simply tables or lists of colors that are organized in some desired fashion. The surface, patch, and image objects can be associated with a custom color map. This article shows you how to create such custom color maps.
To create a custome color map in C#, you need to construct a color map with an m x 4 color map matrix. Each row of this matrix represents ARGB values. The row index can represent the y data of a 2D chart or the height (the z data) of a 3D surface plot. For a given color map matrix with m rows, the color data values can be linearly scaled to the color map.
For example, if you want to use the color map to represent the y coordinates of a 2D graphics object, you can use the YMin and YMax to linearly transform the y data values to indices where each index identifies an ARGB row (i.e., a color) in the color map matrix. The mathematical transformation of the color index values is described by the formula:
Here y is the individual value of Y data and m is the length of the color map matrix. This allows you to use the entire range of colors in the color map over the plotted data. For 3D graphics objects and 3D surface charts, the y data should be replaced with the z data.
Now we can implement the colormap matrix class. Let's use a custom colormap named "Spring" as an example to illustrate how easily it is to create a custom colormap.
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing.Drawing2D;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">namespace Example1_8
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public class ColorMap
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private int colormapLength = 64;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private int alphaValue = 255;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap()
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap(int colorLength)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">colormapLength = colorLength;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap(int colorLength, int alpha)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">colormapLength = colorLength;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">alphaValue = alpha;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public int[,] Spring()
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int[,] cmap = new int[colormapLength, 4];
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">float[] spring = new float[colormapLength];
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">for (int i = 0; i < colormapLength; i++)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">spring[i] = 1.0f * i / (colormapLength - 1);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 0] = alphaValue;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 1] = 255;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 2] = (int)(255 * spring[i]);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 3] = 255 - cmap[i, 1];
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">return cmap;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">......
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
In this class, there are three constructors. If you use
to create a new ColorMap object, the default parameters colormapLength = 64 and alphaValue = 255 will be used. Here colormapLength is the length of the color map matrix and the alphaValue is the color transparency parameter. The default alphaValue of 255 represents an opaque color. The following constructor
overrides the colormapLength with the input parameter 32, and the alphaValue remains the default value of 255. You can override both parameters by calling the ColorMap class with the following code snippet:
This sets colormapLength = 32 and alphaValue = 100.
I have add eight commonly used custom colormaps to the ColorMap class. You can easily add more custom colormaps following the procedure described here.
The ColorMap class can be used in your C# applications. The follwoing Form1 class demonstrate how to draw various color bars using the ColorMap class. Here is the code snippet of the Form1 class:
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Windows.Forms;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">namespace Example1_8
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public partial class Form1 : Form
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public Form1()
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">InitializeComponent();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">SetStyle(ControlStyles.ResizeRedraw, true);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">This.BackColor = Color.White;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">this.Width = 340;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">this.Height = 340;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">protected override void OnPaint(PaintEventArgs e)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">Graphics g = e.Graphics;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int width = 30;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int height = 128;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int y = 10;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">ColorMap cm = new ColorMap();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">Font aFont = new Font("Arial", 20, FontStyle.Bold);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.DrawString("OPAQUE COLOR", aFont, Brushes.Black, 10, 60);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10, y, width, height, cm, "Spring");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 40, y, width, height, cm, "Summer");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 2 * 40, y, width, height, cm, "Autumn");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 3 * 40, y, width, height, cm, "Winter");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 4 * 40, y, width, height, cm, "Jet");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 5 * 40, y, width, height, cm, "Gray");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 6 * 40, y, width, height, cm, "Hot");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 7 * 40, y, width, height, cm, "Cool");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">y = y + 150;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">ColorMap cm1 = new ColorMap(64, 150);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.DrawString("TRANSPARENT COLOR", aFont, Brushes.Black, 10, 210);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10, y, width, height, cm1, "Spring");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 40, y, width, height, cm1, "Summer");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 2 * 40, y, width, height, cm1, "Autumn");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 3 * 40, y, width, height, cm1, "Winter");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 4 * 40, y, width, height, cm1, "Jet");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 5 * 40, y, width, height, cm1, "Gray");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 6 * 40, y, width, height, cm1, "Hot");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 7 * 40, y, width, height, cm1, "Cool");
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private void DrawColorBar(Graphics g, int x, int y,
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int width, int height, ColorMap map, string str)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int[,] cmap = new int[64, 4];
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">switch(str)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Jet":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Jet();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Hot":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Hot();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Gray":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Gray();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Cool":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Cool();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Summer":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Summer();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Autumn":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Autumn();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Spring":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Spring();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Winter":
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Winter();
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int ymin = 0;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int ymax = 32;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int dy = height / (ymax - ymin);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int m = 64;
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">for (int i = 0; i < 32; i++)
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int colorIndex = (int)((i - ymin) *
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">m / (ymax - ymin));
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">SolidBrush aBrush = new SolidBrush(Color.FromArgb(
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[colorIndex, 0], cmap[colorIndex, 1],
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[colorIndex, 2], cmap[colorIndex, 3]));
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.FillRectangle(aBrush, x, y + i * dy, width, dy);
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />
Inside the DrawColorBar method, we draw a color bar by dividing it into 32 sub-rectangles. We thenassign the y data from 0 to 31. The switch statement selects a specified color map matrix. The following code snippet
"Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int colorIndex = (int)((i - ymin) * m / (ymax - ymin));
"MARGIN: 6pt 0in">computes the index of the color map matrix using the Y data. Then we create a SolidBrush object using this color map matrix.
"MARGIN: 6pt 0in">Inside the OnPaint method, we create two ColorMap objects, cm and cm1. cm uses the default parameters: colormapLength = 64 and alphaValue = 255; i.e. the opaque color. The parameters of cm1 are reassigned to colormapLength = 64 and alphaValue = 150, indicating that the color becomes transparent.
"MARGIN: 6pt 0in">This project produces the output shown in the following screenshot, which shows eight different color maps defined in the ColorMap class.
"Colormap/fig1-18.gif" />
This project is from the examples of the new book "Practical C# Charts and Graphics", where you can find more advanced chart and graphics programming for real-world .NET applications. For more information, please visit my website at "http://www.authors.unicadpublish.com/~jack_xu">www.authors.unicadpublish.com/~jack_xu
<h2>About the Author</h2>
Dr. Jack Xu has a Ph.D in theoretical physics. He has over 15 years programming experience in Basic, Fortran, C, C++, Matlab, and C#, specializing in numerical computation methods, algorithms, physical modeling, computer-aided design (CAD) development, graphics user interface, and 3D graphics. Currently, he is responsible for developing commercial CAD tools based on Microsoft .NET framework.
Please read my other articles:
"JackXu.asp">"Draw US Flag using C# and GDI+"
"ColorShading.asp">"Create a Custom Color Shading in C#"
"Sphere.asp">"Spherical Coordinates in C#"